package com.hy.main.utils.tree.unusedrecursion;


import cn.hutool.core.util.ReflectUtil;
import com.hy.main.utils.object.StringUtils;
import com.hy.main.utils.tree.unusedrecursion.annotation.TreeEntity;
import com.hy.main.utils.tree.unusedrecursion.annotation.TreeField;
import com.hy.main.utils.tree.unusedrecursion.enums.FieldType;
import com.hy.main.utils.tree.unusedrecursion.enums.IdType;
import com.hy.main.utils.tree.unusedrecursion.enums.SortType;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

/**
 * @author
 * @description 未使用递归实现树形结构
 * 树结构实体通用工具类
 * 注意pid 和 id 的类型必须一致
 */
public final class TreeUtil {
    /**
     * Map<FieldType String> ---> Map<[注解属性类型,[字段名]>
     */
    private static Map<FieldType, String> fieldsMap = new HashMap<>(8);

    /**
     * @param list      所有列表
     * @param rootNode  根节点
     * @param treeClazz 树状元素类型
     * @param <E>       传入元素
     *
     * @return 树状结构实体
     */
    public static <E> E toTree(List<E> list, E rootNode, Class<E> treeClazz) {
        if (fieldsMap.size() < 6 >> 1) {
            fieldsMap = getFieldsMap(treeClazz);
        }
        Map<Object, E> map = new HashMap<>(2);
        Object id = ReflectUtil.getFieldValue(rootNode, fieldsMap.get(FieldType.ID));
        Object pid = ReflectUtil.getFieldValue(rootNode, fieldsMap.get(FieldType.PID));
        map.put(id, rootNode);
        for (E childNode : list) {
            buildChildNodes(pid, map, childNode, false, false);
        }
        return map.get(id);
    }

    /**
     * 引用写法 绕过递归
     *
     * @param list      所有列表
     * @param pid       父级id
     * @param treeClazz 树状元素类型
     * @param <E>       传入元素
     *
     * @return 树状结构集合
     */
    public static <E> List<E> toTreeList(List<E> list, Object pid, Class<E> treeClazz) {
        if (fieldsMap.size() < 6 >> 1) {
            fieldsMap = getFieldsMap(treeClazz);
        }
        TreeEntity treeEntity = treeClazz.getAnnotation(TreeEntity.class);
        SortType sortType = treeEntity.sortType();
        String sortField = fieldsMap.get(FieldType.SORT);
        boolean sort = false;
        boolean asc = false;
        if (StringUtils.isNotBlank(sortField)) {
            sort = true;
            if (sortType == SortType.ASC) {
                asc = true;
            }
        }
        Map<Object, E> map = new HashMap<>(2);
        List<E> rootNodes = new ArrayList<>();
        for (E o : list) {
            Object tPid = ReflectUtil.getFieldValue(o, fieldsMap.get(FieldType.PID));
            if (tPid.equals(pid)) {
                rootNodes.add(o);
                map.put(pid, o);
            }
        }
        if (sort) {
            treeNodesSort(rootNodes, asc);
        }
        for (E childNode : list) {
            if (childNode != null) {
                buildChildNodes(pid, map, childNode, sort, asc);
            }
        }
        return rootNodes;
    }

    /**
     * 集合排序
     *
     * @param nodes 节点集合
     * @param asc   是否升序
     * @param <E>   类型
     */
    private static <E> void treeNodesSort(List<E> nodes, boolean asc) {
        nodes.sort((o1, o2) -> {
            String o1Sort = ReflectUtil.getFieldValue(o1, fieldsMap.get(FieldType.SORT)).toString();
            String o2Sort = ReflectUtil.getFieldValue(o2, fieldsMap.get(FieldType.SORT)).toString();
            return asc ? o1Sort.compareTo(o2Sort) : o2Sort.compareTo(o1Sort);
        });
    }

    /**
     * 构建子节点集合
     */
    private static <E> void buildChildNodes(Object pid, Map<Object, E> map, E childNode, boolean sort, boolean asc) {
        Object tId = ReflectUtil.getFieldValue(childNode, fieldsMap.get(FieldType.ID));
        map.put(tId, childNode);
        Object tPid = ReflectUtil.getFieldValue(childNode, fieldsMap.get(FieldType.PID));
        if (!tPid.equals(pid)) {
            E parentNode = map.get(tPid);
            List<E> tChildNodes = (List<E>) ReflectUtil.getFieldValue(parentNode, fieldsMap.get(FieldType.CHILD));
            if (tChildNodes == null) {
                tChildNodes = new ArrayList<>();
            }
            tChildNodes.add(childNode);
            if (sort) {
                treeNodesSort(tChildNodes, asc);
            }
            if (parentNode != null) {
                ReflectUtil.setFieldValue(parentNode, fieldsMap.get(FieldType.CHILD), tChildNodes);
            }
        }
    }

    /**
     * 获取 Field Map
     *
     * @param clazz 类名
     *
     * @return Map<FieldType String> ---> Map<[注解属性类型,[字段名]>
     */
    private static <E> Map<FieldType, String> getFieldsMap(Class<E> clazz) {
        if (!clazz.isAnnotationPresent(TreeEntity.class)) {
            throw new RuntimeException("缺少注解: @TreeEntity");
        }
        TreeEntity treeEntity = clazz.getAnnotation(TreeEntity.class);
        IdType idType = treeEntity.idType();
        Stream.of(clazz.getDeclaredFields()).forEach(field -> {
            if (field.isAnnotationPresent(TreeField.class)) {
                TreeField treeField = field.getAnnotation(TreeField.class);
                switch (treeField.fieldType()) {
                    case ID:
                        if (!idType.getClazz().equals(field.getType())) {
                            throw new RuntimeException("ID类型与idType类型不一致");
                        }
                        fieldsMap.put(FieldType.ID, field.getName());
                        break;
                    case PID:
                        if (!idType.getClazz().equals(field.getType())) {
                            throw new RuntimeException("PID类型与idType类型不一致");
                        }
                        fieldsMap.put(FieldType.PID, field.getName());
                        break;
                    case CHILD:
                        fieldsMap.put(FieldType.CHILD, field.getName());
                        break;
                    case SORT:
                        fieldsMap.put(FieldType.SORT, field.getName());
                        break;
                    default:
                        break;
                }
            }
        });
        if (fieldsMap.size() < 6 >> 1) {
            throw new RuntimeException("缺少某一个属性: FieldType.ID | FieldType.PID | FieldType.CHILD)");
        }
        return fieldsMap;
    }
}
